{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d22f900d",
   "metadata": {},
   "source": [
    "## Lesson 3: Binary format modification"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccfbda43",
   "metadata": {},
   "source": [
    "**Objectives**: learn about resource views; find specific children in a resource tree and modify them; manipulate ELF program headers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bbd4eaed",
   "metadata": {},
   "source": [
    "In this lesson, we're going to find the LOAD program header, and change its permission to make it non-executable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "970c50b3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using OFRAK Community License.\n"
     ]
    }
   ],
   "source": [
    "from ofrak import OFRAK\n",
    "from ofrak_tutorial.helper_functions import create_hello_world_binary\n",
    "\n",
    "create_hello_world_binary()\n",
    "\n",
    "ofrak = OFRAK()\n",
    "basic_context = await ofrak.create_ofrak_context()\n",
    "root_resource = await basic_context.create_root_resource_from_file(\"hello_world\")\n",
    "unpack_result = await root_resource.unpack_recursively()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a317bd25",
   "metadata": {},
   "source": [
    "So, after unpacking, our root resource is an ELF resource, right?..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "77914edb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Elf\n",
      "File\n",
      "FilesystemEntry\n",
      "GenericBinary\n",
      "LinkableBinary\n",
      "Program\n"
     ]
    }
   ],
   "source": [
    "for tag in sorted(root_resource.get_tags(), key=str):\n",
    "    print(tag)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd32311d",
   "metadata": {},
   "source": [
    "Yes... But it's also all of the above. What if we want to run ELF-specific methods on it? This is what **resource views** are designed for: they're a way to, as their name suggests, *view* a resource as a particular type for the purpose of executing operations specific to that type."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59be0a46",
   "metadata": {},
   "source": [
    "We can't call the ELF-specific method `get_header` on a generic resource..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8b8dfdb1",
   "metadata": {
    "tags": [
     "nbval-raises-exception"
    ]
   },
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Resource' object has no attribute 'get_header'",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mAttributeError\u001B[0m                            Traceback (most recent call last)",
      "Cell \u001B[0;32mIn[3], line 2\u001B[0m\n\u001B[1;32m      1\u001B[0m \u001B[38;5;66;03m# Will fail with an AttributeError!\u001B[39;00m\n\u001B[0;32m----> 2\u001B[0m elf_header \u001B[38;5;241m=\u001B[39m \u001B[43mroot_resource\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_header\u001B[49m()\n",
      "\u001B[0;31mAttributeError\u001B[0m: 'Resource' object has no attribute 'get_header'"
     ]
    }
   ],
   "source": [
    "# Will fail with an AttributeError!\n",
    "elf_header = root_resource.get_header()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3bd40d9e",
   "metadata": {},
   "source": [
    "... So we create an *ELF view* first to access this ELF-specific functionality:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f51edc49",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ofrak.core import Elf\n",
    "\n",
    "elf_view = await root_resource.view_as(Elf)\n",
    "\n",
    "elf_header = await elf_view.get_header()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c8a3743",
   "metadata": {},
   "source": [
    "And how do we get back the resource from a resource view? By using the `resource` attribute of any resource view."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ce80cdc",
   "metadata": {},
   "source": [
    "Trying to get the children of an ELF view will fail..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4252034d",
   "metadata": {
    "tags": [
     "nbval-raises-exception"
    ]
   },
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Elf' object has no attribute 'get_children'",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mAttributeError\u001B[0m                            Traceback (most recent call last)",
      "Cell \u001B[0;32mIn[5], line 2\u001B[0m\n\u001B[1;32m      1\u001B[0m \u001B[38;5;66;03m# Will fail with an AttributeError!\u001B[39;00m\n\u001B[0;32m----> 2\u001B[0m \u001B[38;5;28;01mawait\u001B[39;00m elf_view\u001B[38;5;241m.\u001B[39mget_children()\n",
      "\u001B[0;31mAttributeError\u001B[0m: 'Elf' object has no attribute 'get_children'"
     ]
    }
   ],
   "source": [
    "# Will fail with an AttributeError!\n",
    "await elf_view.get_children()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a64214f5",
   "metadata": {},
   "source": [
    "... That's what the `resource` attribute is for:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b9a4a89b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Resource(resource_id=bd10b0f7bae3435faa84b0034ccda0e0, tag=[ElfBasicHeader], data=bd10b0f7bae3435faa84b0034ccda0e0)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(await elf_view.resource.get_children())[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5dee5d48",
   "metadata": {},
   "source": [
    "So we're going to use this ELF view for all ELF-related functionality.\n",
    "\n",
    "How would we find the LOAD program header?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "36b61916",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ElfProgramHeader(segment_index=3, p_type=1, p_offset=4096, p_vaddr=4198400, p_paddr=4198400, p_filesz=329, p_memsz=329, p_flags=5, p_align=4096)\n"
     ]
    }
   ],
   "source": [
    "from ofrak_type.memory_permissions import MemoryPermissions\n",
    "from ofrak.core import ElfProgramHeader, ElfProgramHeaderType\n",
    "\n",
    "\n",
    "async def get_exec_load_program_header(elf_view: Elf) -> ElfProgramHeader:\n",
    "    \"\"\"Return the first executable LOAD program header in `elf_view`.\"\"\"\n",
    "    for program_header in await elf_view.get_program_headers():\n",
    "        if (\n",
    "            program_header.p_type == ElfProgramHeaderType.LOAD.value\n",
    "            and program_header.p_flags & MemoryPermissions.X.value\n",
    "        ):\n",
    "            return program_header\n",
    "    raise RuntimeError(f\"Could not find executable LOAD program header in {elf_view}\")\n",
    "\n",
    "\n",
    "exec_load_program_header = await get_exec_load_program_header(elf_view)\n",
    "print(exec_load_program_header)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f03d3c5",
   "metadata": {},
   "source": [
    "Now that we know how to find it, let's see how we can modify it!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "67b51536",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "still executable: p_flags=5\n",
      "modified: p_flags=4\n"
     ]
    }
   ],
   "source": [
    "from ofrak.core import ElfProgramHeaderModifier, ElfProgramHeaderModifierConfig\n",
    "\n",
    "\n",
    "async def make_program_header_noexec(program_header):\n",
    "    await program_header.resource.run(\n",
    "        ElfProgramHeaderModifier,\n",
    "        ElfProgramHeaderModifierConfig(p_flags=program_header.p_flags & ~MemoryPermissions.X.value),\n",
    "    )\n",
    "\n",
    "\n",
    "print(f\"still executable: p_flags={exec_load_program_header.p_flags}\")\n",
    "await make_program_header_noexec(exec_load_program_header)\n",
    "print(f\"modified: p_flags={exec_load_program_header.p_flags}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0731d93",
   "metadata": {},
   "source": [
    "Let's put it all together now. As a reminder, here's the typical OFRAK workflow we were mentioning in [Lesson 2](2_ofrak_internals.ipynb):\n",
    "\n",
    "- create an OFRAK resource from something, typically a file on disk\n",
    "  + **unpack** the resource\n",
    "    - **modify** the resource\n",
    "  + re-**pack** the resource\n",
    "- export the modified and repacked resource, typically to a file on disk\n",
    "\n",
    "See if you can spot it in the code below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "175afd26",
   "metadata": {},
   "outputs": [],
   "source": [
    "async def make_elf_load_header_noexec(ofrak_context, input_filename, output_filename):\n",
    "    root_resource = await ofrak_context.create_root_resource_from_file(input_filename)\n",
    "    await root_resource.unpack_recursively()\n",
    "\n",
    "    elf_view = await root_resource.view_as(Elf)\n",
    "    exec_load_program_header = await get_exec_load_program_header(elf_view)\n",
    "    await make_program_header_noexec(exec_load_program_header)\n",
    "\n",
    "    await root_resource.pack()\n",
    "    await root_resource.flush_data_to_disk(output_filename)\n",
    "\n",
    "\n",
    "await make_elf_load_header_noexec(basic_context, \"hello_world\", \"nohello_world\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "f53d7f52",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Segmentation fault (core dumped)\r\n"
     ]
    }
   ],
   "source": [
    "# Expecting a segmentation fault...\n",
    "!chmod +x nohello_world && ./nohello_world"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6c732af",
   "metadata": {},
   "source": [
    "[Next page](4_simple_code_modification.ipynb)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.18"
  },
  "pycharm": {
   "stem_cell": {
    "cell_type": "raw",
    "metadata": {
     "collapsed": false
    },
    "source": []
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
